/* ************************************************************************ */
/*                                                                          */
/*  Copyright (c)2007 Mark Winterhalder | mark_at_snafoo.org                */
/*                                                                          */
/* This library is free software; you can redistribute it and/or            */
/* modify it under the terms of the GNU Lesser General Public               */
/* License as published by the Free Software Foundation; either             */
/* version 3.0 of the License, or (at your option) any later version.       */
/*                                                                          */
/* This library is distributed in the hope that it will be useful,          */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of           */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU        */
/* Lesser General Public License or the LICENSE file for more details.      */
/*                                                                          */
/* ************************************************************************ */

package altdate;

class Doomsday {
#if neko
	static function main () : Void {
		var args = neko.Sys.args();
		neko.Lib.println( Doomsday.weekdates( Std.parseInt( args.shift() ), Std.parseInt( args.shift() ) -1, Std.parseInt( args.shift() ) ) );
	}
#end

	public static function weekdates ( year : Int, month : Int, weekday : Int ) : Array<Int> {
		var dd = new Doomsday( year );
		var dayOfFirst = dd.weekday( month, 1 );
		var first = 1 + ((weekday - dayOfFirst + 7) % 7);

		var lim = dd.daysPerMonth[ month ];
		var day = first;
		var result = new Array<Int>();
		result.push( first );
		while( (day += 7) <= lim ) {
			result.push( day );
		}

		return result;
	}

	public static function dayOfWeek ( year : Int, month : Int, day : Int ) : Int {
		var dd = new Doomsday( year );
		return dd.weekday( month, day );
	}

	public static function daysInMonth ( year : Int, month : Int ) : Int {
		return (new Doomsday( year )).daysPerMonth[ month ];
	}


	static var anchorDays : Array<Int> = [ 2, 7, 5, 3 ];

	var doomsday : Int;
	var doomsdayInMonth : Array<Int>;
	var daysPerMonth : Array<Int>;

	private function new ( year : Int ) {
		// anchor day
		var anchorDay = anchorDays[ Math.floor( year / 100 ) % 4 ];

		// doomsday
		var centYear = year % 100;
		doomsday = ((Math.floor( centYear / 12 ) + (centYear % 12) + Math.floor( (centYear % 12) / 4 )) % 7 + anchorDay) % 7;

		// leap year?
		var leapYear : Bool;
		if( year % 4 == 0 ) {
			if( year % 100 == 0 ) {
				if( year % 400 == 0 ) {
					leapYear = true;
				} else {
					leapYear = false;
				}
			} else {
				leapYear = true;
			}
		} else {
			leapYear = false;
		}

		// doomsday in a given month
		if( leapYear ) {
			doomsdayInMonth = [ 3, 0, 6, 3, 1, 5, 3, 0, 4, 2, 6, 4, 1, 5 ];
			daysPerMonth = [ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ];
		} else {
			doomsdayInMonth = [ 2, 6, 6, 3, 1, 5, 3, 0, 4, 2, 6, 4, 1, 5 ];
			daysPerMonth = [ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ];
		}
	}

	/**
		month 0-11
		day 1-31
	*/
	function weekday ( month : Int, day : Int ) : Int {
		var dim = doomsdayInMonth[ month ];
		var offset = (day - dim + 6) % 7;

		return (offset + doomsday) % 7;
	}
}
